﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net.Mail;
using System.Text;
using System.Threading;
using CSW.Framework.Common.Utils;
using CSW.Framework.Common.Utils.Mail;

namespace CSW.Framework.Common.Exceptions
{
    /// <summary>
    /// ExceptionHelper.
    /// </summary>
    public static class ExceptionHelper
    {
        /// <summary>
        /// Occurs when an exception is raised.
        /// </summary>
        public static event EventHandler<DataEventArgs<ExceptionDetails>> ExceptionRaised;

        /// <summary>
        /// Gets or sets a value indicating whether to write exceptions to the event log.
        /// </summary>
        /// <value><c>true</c> to write exceptions to the event log; otherwise, <c>false</c>.</value>
        public static bool WriteToEventLog { get; set; }

        /// <summary>
        /// Gets or sets the SMTP server.
        /// </summary>
        /// <value>The SMTP server.</value>
        public static string SmtpServer { get; set; }

        /// <summary>
        /// Gets or sets the "From" address.
        /// </summary>
        /// <value>The "From" address.</value>
        public static string SmtpFromAddress { get; set; }

        /// <summary>
        /// Gets or sets the exception notify list.
        /// </summary>
        /// <value>The exception notify list.</value>
        public static ICollection<string> SmtpNotifyAddressList { get; set; }

        /// <summary>
        /// Handles the ThreadException event of the Application control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Threading.ThreadExceptionEventArgs"/> instance containing the event data.</param>
        public static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
        {
            ShowException(e.Exception, false);
        }

        /// <summary>
        /// Shows the exception.
        /// </summary>
        /// <param name="e">The exception.</param>
        public static void ShowException(Exception e)
        {
            ShowException(e, false);
        }

        /// <summary>
        /// Shows the exception.
        /// </summary>
        /// <param name="e">The exception.</param>
        /// <param name="occuredDuringStartup">if set to <c>true</c>, indicates the exception occured during startup.</param>
        public static void ShowException(Exception e, bool occuredDuringStartup)
        {
            bool isFatal = false;

            StringBuilder shortErrMsg = new StringBuilder();
            string longErrorMessage = e.ToString();

            // Extract all inner exceptions
            Exception curException = e;
            do
            {
                if (curException.Message == "Value Dispose() cannot be called while doing CreateHandle()." ||
                    curException.Message == "Error creating window handle.")
                {
                    if (occuredDuringStartup)
                        isFatal = true;
                }

                if (curException is NonRecoverableException)
                {
                    isFatal = true;
                }

                shortErrMsg.AppendFormat("{0}{1}{1}", curException.Message, Environment.NewLine);
                curException = curException.InnerException;

            } while (curException != null);

            longErrorMessage += string.Format("{0}{0}Raised by: {1}", Environment.NewLine, Environment.UserName);

            // Attempt to write to the EventLog
            if (WriteToEventLog)
            {
                try
                {
                    EventLog log = new EventLog();
                    log.Source = string.Format("{0} {1}", EntryAssembly.ProductName, EntryAssembly.ProductVersion);
                    log.WriteEntry(shortErrMsg + longErrorMessage, EventLogEntryType.Error);
                }
                catch (Exception ex)
                {
                    // Writing to event log failed
                    Trace.WriteLine(ex);
                }
            }

            // The Exception was most likely caused by a rapid startup/shutdown sequence.  Exit the program.
            if (isFatal && occuredDuringStartup)
                Environment.Exit(-1);

            ShowException(e, shortErrMsg.ToString(), longErrorMessage, occuredDuringStartup);

            if (isFatal)
                Environment.Exit(-1);
        }

        private static void ShowException(Exception exception, string shortErrMsg, string stackTrace, bool occuredDuringStartup)
        {
            if (!string.IsNullOrEmpty(SmtpServer))
            {
                try
                {
                    string subject = string.Format("{0} {1} Exception", EntryAssembly.ProductName, EntryAssembly.ProductVersion);
                    string body = shortErrMsg + stackTrace;
                    Email.Send(SmtpServer, SmtpFromAddress, SmtpNotifyAddressList, subject, body, MailFormat.Basic, MailPriority.High);
                }
                catch (Exception ex)
                {
                    Trace.WriteLine(ex);
                }
            }

            EventHandler<DataEventArgs<ExceptionDetails>> exceptionRaised = ExceptionRaised;
            if (exceptionRaised != null)
                exceptionRaised(null, new DataEventArgs<ExceptionDetails>(new ExceptionDetails(exception, shortErrMsg, stackTrace, occuredDuringStartup)));
        }
    }
}
